package mop.xml;

import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import mop.model.World;
import mop.model.project.Project;
import mop.model.resource.Resource;
import mop.model.task.Task;
import mop.model.user.User;
import mop.model.user.UserType;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * XML reader (SAX) for the MOP project, iteratie 2.
 * 
 * @author 	MOP Team 7
 */
public class TaskplannerXMLReader extends DefaultHandler
{
	/*public static void main(String args[])
	{
		World world = new World();
		TaskplannerXMLReader handler = new TaskplannerXMLReader(world);
		File xmlFile = new File("C:\\students_public.xml");
		handler.process(xmlFile);
		System.out.println("\nAll done!");
	}*/
	
	// debug mode on = true
	private final boolean DEBUG_MODE = false;
	
	// reference to World object
	private World world;
	
	// parser object
	private SAXParser parser;
	
	/**
	 * Constructor for this SAX parser.
	 * 
	 * Initializes the parser object and the "tags" hashmap 
	 * containing the booleans indicating which elements are being read.
	 * 
	 * @param	world
	 * @post	world reference is set.
	 * @post	parser object is set.
	 * @post	tags hashmap is set to an empty map.
	 * @post	projectIds hashmap is set to an empty map.
	 * @post	taskIds hashmap is set to an empty map.
	 * @post	resourceIds hashmap is set to an empty map.
	 */
	public TaskplannerXMLReader(World world)
	{
		this.world = world;
		this.parser = null;
		this.tags = new HashMap<String, Boolean>();
		
		this.projectIds = new HashMap<String, Project>();
		this.taskIds = new HashMap<String, Task>();
		this.resourceIds = new HashMap<String, Resource>();
		
		this.lastTaskReqRes = new LinkedList<Resource>();
		this.lastTaskDepTasks = new LinkedList<Task>();
	}
	
	/**
	 * Starts the processing of an XML file.
	 * 
	 * @param 	file
	 * 			An XML file as a File object.
	 */
	public void process(File file)
	{
		SAXParserFactory spf = SAXParserFactory.newInstance();
		spf.setNamespaceAware(true);
		spf.setValidating(true);
		
		// Make SAX parser
		
		try {
			parser = spf.newSAXParser();
		} catch (SAXException e) {
			e.printStackTrace(System.err);
			System.exit(1);
		} catch (ParserConfigurationException e) {
			e.printStackTrace(System.err);
			System.exit(1);
		}
		
		// Start parsing
		
		try {
			parser.parse(file, this);
		} catch (IOException e) {
			e.printStackTrace(System.err);
		} catch (SAXException e) {
			e.printStackTrace(System.err);
		}
	}
	
	/**
	 * @see		DefaultHandler
	 */
	public void startDocument()
	{
	}
	
	/**
	 * @see		DefaultHandler
	 */
	public void endDocument()
	{
	}
	
	// Booleans indicating which tag is "active".
	private HashMap<String, Boolean> tags;
	
	/**
	 * Returns whether the given element (String) is active or not.
	 * 
	 * @param 	el
	 * 			A string representing an XML element.
	 * @return	True if the tag was recently read (and the end tag not yet), else false.
	 */
	private boolean isElementActive(String el)
	{
		return (tags.get(el) != null && tags.get(el));
	}
	
	/**
	 * @see		DefaultHandler
	 */
	public void startElement(String uri, String localName, String qname, Attributes attr)
	{
		tags.put(localName, true);
		if (attr != null && attr.getLength() > 0)
			lastId = attr.getValue("id");
	}
	
	/**
	 * @see		DefaultHandler
	 */
	public void endElement(String uri, String localName, String qname)
	{
		tags.put(localName, false);
		
		// make task?
		if (localName.equals("task"))
		{
			try {
				Task t = new Task(lastTaskDescription, lastTaskStart, lastTaskDuration, 
					lastTaskEnd, lastTaskDepTasks, lastTaskReqRes, lastUser);
				t.forceStatus(lastTaskStatus);
				t.setProject(lastTaskProject);
				taskIds.put(lastId, t);
				
				lastTaskReqRes = new LinkedList<Resource>();
				lastTaskDepTasks = new LinkedList<Task>();
			} catch (Throwable t) {
				System.out.println("An exception occurred while reading XML!\n" + t.toString());
				t.printStackTrace(System.err);
			}
		}
		
		// make reservation?
		if (localName.equals("reservation"))
		{
			try {
				lastReservationResource.addReservation(lastReservationStart, lastReservationEnd);
			} catch (Throwable t) {
				System.out.println("An exception occurred while reading XML!\n" + t.toString());
				t.printStackTrace(System.err);
			}
		}
	}
	
	/**
	 * @see		DefaultHandler
	 */
	public void characters(char[] ch, int start, int length)
	{
		String s = new String(ch,start,length).trim();
		
		// Element is project?
		if (isElementActive("project"))
		{
			if (isElementActive("description"))
			{
				try {
					projectIds.put(lastId, new Project(world.getProjectDataBase(), s));
				} catch (Throwable t) {
					System.out.println("An exception occurred while reading XML!\n" + t.toString());
					t.printStackTrace(System.err);
				}
				
				if (this.DEBUG_MODE) {
					System.out.println("Project description: " + s);
					System.out.println("#Project id: " + lastId);
				}
			}
		}		 
		
		// Element is resource?
		if (isElementActive("resources") && isElementActive("resource"))
		{
			if (isElementActive("description"))
			{
				lastResourceDescription = s;
				
				if (this.DEBUG_MODE)
					System.out.println("Resource description: " + s);
			}
			
			if (isElementActive("type"))
			{
				try {
					resourceIds.put(lastId, 
						ResourceFactory.createResource(
								world.getResourceDataBase(), 
								lastResourceDescription, s));
				} catch (Throwable t) {
					System.out.println("An exception occurred while reading XML!\n" + t.toString());
					t.printStackTrace(System.err);
				}
				
				if (this.DEBUG_MODE) {
					System.out.println("-> Resource type: " + s);
					System.out.println("#Resource id: " + lastId);
				}
			}
		}
		
		/**
		 * !!!!!!!!!!!!!!!!!!!!!!!!
		 * TODO update this for types
		 * currently uses dud
		 */
		UserType ut = null;
		try
		{
			ut = new UserType(world.getUserTypeDataBase(),"Update XML NOW !!");
		}catch(Throwable e){ };
		
		// Element is user?
		if (isElementActive("user"))
		{
			if (isElementActive("name"))
			{
				try {
					lastUser = new User(ut,s, world.getUserDataBase());
				} catch (Throwable t) {
					System.out.println("An exception occurred while reading XML!\n" + t.toString());
					t.printStackTrace(System.err);
				}
				
				if (this.DEBUG_MODE)
					System.out.println("#User name: " + s);
			}
			
			if (isElementActive("task"))
			{
				if (isElementActive("description"))
				{
					lastTaskDescription = s;
					
					if (this.DEBUG_MODE)
						System.out.println("-> Has Task: " + s);
				}
				
				if (isElementActive("startDate"))
				{
					if (this.DEBUG_MODE)
						System.out.println("--> Task startDate: " + s);
					
					// parse date
					try {
						Date d = TimeParser.parse(s);
						lastTaskStart = new Timestamp(d.getTime());
						if (this.DEBUG_MODE)
							System.out.println("--> Parsed date: " + d.toString());
					} catch (Exception e) {
						System.out.println("Error parsing!");
					}
				}
				
				if (isElementActive("endDate"))
				{
					if (this.DEBUG_MODE)
						System.out.println("--> Task endDate: " + s);
					
					// parse date
					try {
						Date d = TimeParser.parse(s);
						lastTaskEnd = new Timestamp(d.getTime());
						if (this.DEBUG_MODE)
							System.out.println("--> Parsed date: " + d.toString());
					} catch (Exception e) {
						System.out.println("Error parsing!");
					}
				}
				
				if (isElementActive("duration"))
				{
					long ms = Long.parseLong(s) * 60000L;
					lastTaskDuration = new Timestamp(ms);
					if (this.DEBUG_MODE)
						System.out.println("--> Task duration: " + s);
				}
				
				if (isElementActive("status"))
				{
					lastTaskStatus = s;
					if (this.DEBUG_MODE)
						System.out.println("--> Task status: " + s);
				}
				
				if (isElementActive("refProject"))
				{
					lastTaskProject = projectIds.get(s);
					if (this.DEBUG_MODE)
						System.out.println("--> Task refProject: " + s);
				}
				
				if (isElementActive("requiredResources") && isElementActive("refResource"))
				{
					lastTaskReqRes.add(resourceIds.get(s));
					if (this.DEBUG_MODE)
						System.out.println("--> Task requires (refResource): " + s);
				}
				
				if (isElementActive("dependsOn") && isElementActive("refTask"))
				{
					lastTaskDepTasks.add(taskIds.get(s));
					if (this.DEBUG_MODE)
						System.out.println("--> Task depends on (refTask): " + s);
				}
			}
		}
		
		// Element is reservation?
		if (isElementActive("reservations") && isElementActive("reservation"))
		{
			if (isElementActive("time"))
			{
				if (this.DEBUG_MODE)
					System.out.println("Reservation on (time): " + s);
				
				// parse date
				try {
					Date d = TimeParser.parse(s);
					lastReservationStart = new Timestamp(d.getTime());
					if (this.DEBUG_MODE)
						System.out.println("--> Parsed date: " + d.toString());
				} catch (ParseException e) {
					System.out.println("Error parsing!");
				}
			}
			
			if (isElementActive("duration"))
			{
				// Get duration
				Date d = new Date();
				d.setMinutes(Integer.parseInt(s));
				
				// Add to start to calculate end
				lastReservationEnd = new Timestamp(lastReservationStart.getTime() + d.getTime());
				
				if (this.DEBUG_MODE)
					System.out.println("-> Duration: " + s);
			}
			
			if (isElementActive("refResource"))
			{
				lastReservationResource = resourceIds.get(s);
				if (this.DEBUG_MODE)
					System.out.println("-> For resource (refResource): " + s);
			}
		}
	}
	
	// this is all for remembering stuff
	
	private String lastResourceDescription;
	
	private User lastUser;
	
	private String lastTaskDescription;
	private Timestamp lastTaskStart;
	private Timestamp lastTaskEnd;
	private Timestamp lastTaskDuration;
	private String lastTaskStatus;
	private Project lastTaskProject;
	private LinkedList<Resource> lastTaskReqRes;
	private LinkedList<Task> lastTaskDepTasks;
	
	private Timestamp lastReservationStart;
	private Timestamp lastReservationEnd;
	private Resource lastReservationResource;	
	
	// mapping "xml ids" to models
	private String lastId;
	private HashMap<String, Project> projectIds;
	private HashMap<String, Task> taskIds;
	private HashMap<String, Resource> resourceIds;
}